/*************************************************************************
 *                    free                     *
 *************************************************************************
 *************************************************************************
 * Filename	   : table_pmt.c
 * Description   : This file contains the functions to do with the PMT
 * Version	   : 1.0
 * History       :
 free 2013-4-15  Create
 *************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include "section.h"
#include "table_pat.h"
#include "table_pmt.h"


#define PMT_TABLE_ID		0x02

/*************************************************************************
 * Function Name : ParseElement
 * Description   : get one element from a byte array
 * Parameters    :
 * Returns       :
 **************************************************************************/
static void ParseElement(PROGRAM_ELEMENT *pstProgramElement, unsigned char *pucBuffer)
{
	pstProgramElement->stream_type = pucBuffer[0];
	pstProgramElement->reserved1 = (pucBuffer[1] >> 5) & 0x07;
	pstProgramElement->elementary_PID = ((pucBuffer[1] & 0x1f) << 8) | pucBuffer[2];
	pstProgramElement->reserved2 = (pucBuffer[3] >> 4) & 0x0f;
	pstProgramElement->ES_info_length = ((pucBuffer[3] & 0x0f) << 8) | pucBuffer[4];
	pstProgramElement->pstNext = NULL;
}

/*************************************************************************
 * Function Name : GetElementList
 * Description   : get all elements of a PMT section from a byte array
 * Parameters    :
 * Returns       :
 **************************************************************************/
static PROGRAM_ELEMENT *GetElementList(unsigned char *pucBuffer, int iElementsSize)
{
	int iReadCounter = 0;
	PROGRAM_ELEMENT *pstElements = NULL;
	PROGRAM_ELEMENT *pstTemp = NULL;
	PROGRAM_ELEMENT *pstNew = NULL;

	while (iReadCounter < iElementsSize)
	{
		pstNew = (PROGRAM_ELEMENT *)malloc(sizeof(PROGRAM_ELEMENT));
		ParseElement(pstNew, pucBuffer);
		if (pstElements == NULL )
		{
			pstElements = pstNew;
		}
		else
		{
			pstTemp->pstNext = pstNew;
		}
		pstTemp = pstNew;
		iReadCounter += (5 + pstNew->ES_info_length);
		pucBuffer += (5 + pstNew->ES_info_length);
	}
	return pstElements;
}

/*************************************************************************
 * Function Name : FreeElementList
 * Description   : free all elements of a PMT section
 * Parameters    :
 * Returns       :
 **************************************************************************/
static void FreeElementList(PROGRAM_ELEMENT **ppstElement)
{
	PROGRAM_ELEMENT *pstTemp = NULL;
	while ((*ppstElement) != NULL )
	{
		pstTemp = *ppstElement;
		*ppstElement = (*ppstElement)->pstNext;
		free(pstTemp);
	}
}

/*************************************************************************
 * Function Name : GetSectionPmt
 * Description   : get one PMT section from a byte array
 * Parameters    :
 * Returns       :
 **************************************************************************/
static SECTION_PMT *GetSectionPmt(unsigned char *pucBuffer)
{
	SECTION_PMT *pstSectionPmt = (SECTION_PMT *)malloc(sizeof(SECTION_PMT));
	pstSectionPmt->table_id = pucBuffer[0];
	pstSectionPmt->section_syntax_indicator = (pucBuffer[1] >> 7) & 0x01;
	pstSectionPmt->reserved_future_use = (pucBuffer[1] >> 6) & 0x1;
	pstSectionPmt->reserved1 = (pucBuffer[1] >> 4) & 0x3;
	pstSectionPmt->section_length = ((pucBuffer[1] & 0x0F) << 8) | pucBuffer[2];
	pstSectionPmt->program_number = (pucBuffer[3] << 8) | pucBuffer[4];
	pstSectionPmt->reserved2 = (pucBuffer[5] >> 6) & 0x03;
	pstSectionPmt->version_number = (pucBuffer[5] >> 1) & 0x1F;
	pstSectionPmt->current_next_indicator = pucBuffer[5] & 0x01;
	pstSectionPmt->section_number = pucBuffer[6];
	pstSectionPmt->last_section_number = pucBuffer[7];
	pstSectionPmt->reserved3 = (pucBuffer[8] >> 5) & 0x07;
	pstSectionPmt->PCR_PID = ((pucBuffer[8] & 0x1f) << 8) | pucBuffer[9];
	pstSectionPmt->reserved4 = (pucBuffer[10] >> 4) & 0x0f;
	pstSectionPmt->program_info_length = ((pucBuffer[10] & 0x0f) << 8) | pucBuffer[11];
	pstSectionPmt->pstProgramElements = GetElementList(pucBuffer + 12 + pstSectionPmt->program_info_length,
	        pstSectionPmt->section_length - 9 - 4 - pstSectionPmt->program_info_length);
	pstSectionPmt->pstNext = NULL;
	return pstSectionPmt;
}

/*************************************************************************
 * Function Name : GetTablePmt
 * Description   : get all PMT sections from a TS file according to the pid and link them to a list
 * Parameters    :
 * Returns       :
 **************************************************************************/
SECTION_PMT *GetTablePmt(FILE *pInputFile, long int liFirstPackageOffset, int iPackageLength, int iPackagePID)
{
	SECTION_PMT *pstTablePat = NULL;
	SECTION_PMT *pstTemp = NULL;
	SECTION_PMT *pstNew = NULL;
	SECTION *pstSection = NULL;
	SECTION *pstSectionTemp = NULL;

	pstSection = GetSectionList(pInputFile, liFirstPackageOffset, iPackageLength, iPackagePID, PMT_TABLE_ID);
	pstSectionTemp = pstSection;
	while (pstSectionTemp != NULL )
	{
		pstNew = GetSectionPmt(pstSectionTemp->pucData);
		if (pstTablePat == NULL )
		{
			pstTablePat = pstNew;
		}
		else
		{
			pstTemp->pstNext = pstNew;
		}
		pstTemp = pstNew;
		pstSectionTemp = pstSectionTemp->pstNext;
	}
	FreeSectionList(&pstSection);
	return pstTablePat;
}

/*************************************************************************
 * Function Name : FreeTablePmt
 * Description   : free all the all pmt sections in a PMT table and their elements
 * Parameters    :
 * Returns       :
 **************************************************************************/
void FreeTablePmt(SECTION_PMT **ppstSectionPmt)
{
	SECTION_PMT *pstTemp = NULL;
	while ((*ppstSectionPmt) != NULL )
	{
		pstTemp = *ppstSectionPmt;
		*ppstSectionPmt = (*ppstSectionPmt)->pstNext;
		FreeElementList(&(pstTemp->pstProgramElements));
		free(pstTemp);
	}
}

/*************************************************************************
 * Function Name : GetTableListPmt
 * Description   : get all PMT section lists from a TS file according to the PAT and link them to a list
 * Parameters    :
 * Returns       :
 **************************************************************************/
PMT_TABLELIST *GetTableListPmt(SECTION_PAT *pstTablePat, FILE *pInputFile, long int liFirstPackageOffset, int iPackageLength)
{
	PMT_TABLELIST *pstPmtTableList = NULL;
	PMT_TABLELIST *pstTemp = NULL;
	PMT_TABLELIST *pstNew = NULL;
	PROGRAM *pstProgram = NULL;
	SECTION_PMT *pstSectionPmt = NULL;

	while (pstTablePat != NULL )
	{
		pstProgram = pstTablePat->pstPrograms;
		while (pstProgram != NULL )
		{
			if ((pstProgram->program_number) == 0)
			{
				pstProgram = pstProgram->pstNext;
				continue;
			}
			pstSectionPmt = GetTablePmt(pInputFile, liFirstPackageOffset, iPackageLength, pstProgram->PID);
			pstNew = (PMT_TABLELIST*)malloc(sizeof(PMT_TABLELIST));
			pstNew->pstSectionPmt = pstSectionPmt;
			pstNew->pstNext = NULL;
			if (pstPmtTableList == NULL )
			{
				pstPmtTableList = pstNew;
			}
			else
			{
				pstTemp->pstNext = pstNew;
			}
			pstTemp = pstNew;
			pstProgram = pstProgram->pstNext;
		}
		pstTablePat = pstTablePat->pstNext;
	}
	return pstPmtTableList;
}

/*************************************************************************
 * Function Name : FreeTableListPmt
 * Description   : free all the all tables and their elements in the table list
 * Parameters    :
 * Returns       :
 **************************************************************************/
void FreeTableListPmt(PMT_TABLELIST **ppstTableList)
{
	PMT_TABLELIST *pstTemp = NULL;
	while ((*ppstTableList) != NULL )
	{
		pstTemp = *ppstTableList;
		*ppstTableList = (*ppstTableList)->pstNext;
		FreeTablePmt(&(pstTemp->pstSectionPmt));
		free(pstTemp);
	}
}

/*************************************************************************
 * Function Name : GetPmtByProgramNumber
 * Description   : get one table from PMT table list by program number
 * Parameters    :
 * Returns       :
 **************************************************************************/
SECTION_PMT *GetPmtByProgramNumber(PMT_TABLELIST *pstPmtTableList, int iProgramNumber)
{
	SECTION_PMT *pstSectionPmt = NULL;

	if (iProgramNumber < 0)
	{
		return NULL ;
	}
	while (pstPmtTableList != NULL )
	{
		pstSectionPmt = pstPmtTableList->pstSectionPmt;
		while (pstSectionPmt != NULL )
		{
			if (pstSectionPmt->program_number == iProgramNumber)
			{
				return pstSectionPmt;
			}
			pstSectionPmt = pstSectionPmt->pstNext;
		}
		pstPmtTableList = pstPmtTableList->pstNext;
	}
	return NULL ;
}
